package com.dingreading.cloud.admin.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dingreading.cloud.admin.dto.*;
import com.dingreading.cloud.admin.dto.vo.ShopCartAndOutGoodsVo;
import com.dingreading.cloud.admin.entity.*;
import com.dingreading.cloud.admin.feign.PayOkFeignService;
import com.dingreading.cloud.admin.service.*;
import com.dingreading.cloud.admin.util.AdminStaffRoleUtil;
import com.dingreading.cloud.admin.util.bean.OrderFormV3Util;
import com.dingreading.cloud.admin.util.bean.PurchaseUtil;
import com.dingreading.cloud.admin.util.redis.RedisService;
import com.dingreading.cloud.common.base.R;
import com.dingreading.cloud.common.util.*;
import com.dingreading.cloud.common.util.excel.ExcelHandler;
import com.dingreading.cloud.common.util.qyweixin.AccessTokenDto;
import com.dingreading.cloud.common.util.qyweixin.QyWeiXinUtil;
import com.dingreading.cloud.common.util.qyweixin.QyWeixinMsg;
import com.google.common.collect.Lists;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@RefreshScope
@RestController
@RequestMapping(value = "/system/orderForm/v3")
@Api(tags = "总部-商城订单管理v3")
public class OrderFormControllerV3 {

    @Value("${serverUrl}")
    private String serverUrl;

    @Value("${qyweixin.corpid}")
    private String corpid;
    @Value("${qyweixin.secret}")
    private String secret;
    @Value("${qyweixin.agentid}")
    private String agentid;
    @Value("${qyweixin.thirdUserid}")
    private String thirdUserid;
    @Value("${qyweixin.mallchatid}")
    private String mallchatid;
    @Value("${qyweixin.financechatid}")
    private String financechatid;

    @Resource
    protected OrderFormService orderFormService;
    @Resource
    protected OrderFormLogService orderFormLogService;
    @Resource
    protected OrderFormImageService orderFormImageService;
    @Resource
    protected ShopCartService shopCartService;
    @Resource
    protected GoodsTypeService goodsTypeService;
    @Resource
    protected WhStockOutService stockOutService;
    @Resource
    protected WhStockOutGoodsService stockOutGoodsService;
    @Resource
    protected WhStockOutDeliveryService stockOutDeliveryService;
    @Resource
    protected WhStockOutDeliveryImgService stockOutDeliveryImgService;
    @Resource
    protected CouponStaffService couponStaffService;
    @Resource
    protected CouponStaffLogService couponStaffLogService;
    @Resource
    protected BalanceLogService balanceLogService;
    @Resource
    protected StaffService staffService;
    @Resource
    protected HeadStaffService headStaffService;
    @Resource
    protected WhStockInService stockInService;
    @Resource
    protected StoresService storesService;
    @Resource
    protected GoodsWetPriceService goodsWetPriceService;
    @Resource
    protected AdminRoleStaffService adminRoleStaffService;
    @Resource
    private RedisService redisService;
    @Resource
    private PayOkFeignService payOkFeignService;

    @ApiOperation(value = "分页列表")
    @PostMapping(value = "/pageList", produces = "application/json; charset=UTF-8")
    public R<Object> pageList(
            @ApiParam(value = "分页公共类") PageUtil pageUtil,
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号/用户姓名/手机号") @RequestParam(value = "keyword", required = false) String keyword,
            @ApiParam(value = "商品名称") @RequestParam(value = "goodsName", required = false) String goodsName,
            @ApiParam(value = "项目类型") @RequestParam(value = "projectType", required = false) Integer projectType,
            @ApiParam(value = "支付类型") @RequestParam(value = "payChannelCode", required = false) String payChannelCode,
            @ApiParam(value = "角色类型 1.仓库 2.财务 3.经理 4.采购") @RequestParam(value = "roleType", required = false) Integer roleType,
            @ApiParam(value = "状态 0.取消 1.完成 2.待确认 3.待付款 4.待发货") @RequestParam(value = "status", required = false) Integer status,
            @ApiParam(value = "财务审核状态 90.待财务审核 91.财务审核通过 92.财务审核拒绝 99.系统审核通过") @RequestParam(value = "financeStatus", required = false) Integer financeStatus,
            @ApiParam(value = "开始日期 yyyy-MM-dd") @RequestParam(value = "beginDate", required = false) String beginDate,
            @ApiParam(value = "结束日期 yyyy-MM-dd") @RequestParam(value = "endDate", required = false) String endDate
    ) {
        PageDto<OrderForm> page = orderFormService.pageListV2(
                pageUtil, keyword, projectType, roleType, status, beginDate, endDate, payChannelCode, financeStatus, goodsName
        );

        List<OrderForm> list = page.getList();
        if (CollectionUtils.isNotEmpty(list)) {
            // 获取银行转账图片
            List<String> bankCodes = list.stream()
                    .filter(orderForm -> orderForm.getProjectType().equals(EnumType.BalanceProject.BUY_GOODS.getType())
                            && (StringUtils.isNotBlank(orderForm.getPayChannelCode()) && orderForm.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()))
                    )
                    .map(OrderForm::getCode)
                    .collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(bankCodes)) {
                List<OrderFormImage> imageList = orderFormImageService.getByOrderCodes(bankCodes);
                if (CollectionUtils.isNotEmpty(imageList)) {
                    Map<String, List<OrderFormImage>> map = imageList.stream().collect(Collectors.groupingBy(OrderFormImage::getOrderCode));
                    for (OrderForm data : list) {
                        data.setImageList(map.get(data.getCode()));
                    }
                }
            }
        }

        PageBeanDto data = new PageBeanDto();
        data.setTotal(page.getTotal());
        data.setList(list);
        return R.ok(data);
    }

    @ApiOperation(value = "获取订单详情")
    @PostMapping(value = "/get", produces = "application/json; charset=UTF-8")
    public R<Object> get(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code
    ) {
        // 员工是否是财务
        List<String> identifyList = adminRoleStaffService.identifyListByStaffUid(headStaffUid);
        boolean finance = AdminStaffRoleUtil.isFinance(identifyList);

        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("订单获取失败");

        // 获取正常商品和组合子商品，排除组合商品
        List<ShopCartDto> carts = shopCartService.listByCodeAndComboType(code, 0);

        // 查看银行转账截图
        List<OrderFormImage> imageList = orderFormImageService.getByOrderCode(code);

        // 获取快递信息
        JSONArray express = new JSONArray();
        if (StringUtils.isNotBlank(form.getExpressCode())) {
            // 旧版订单快递信息
            List<String> strings = Arrays.stream(StringUtils.split(form.getExpressCode(), ";")).collect(Collectors.toList());
            for (String string : strings) {
                if (StringUtils.isNotBlank(string)) {
                    String[] split = StringUtils.split(string, "*");
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("expressCode", split[0]);
                    jsonObject.put("expressName", split[1]);
                    jsonObject.put("expressPrice", null);
                    jsonObject.put("expressTime", null);
                    jsonObject.put("deliveryWarehouseName", "");
                    jsonObject.put("staffName", "");
                    jsonObject.put("goodsList", new JSONArray());
                    express.add(jsonObject);
                }
            }
        }
        // 获取发货完成的出库单
        Map<Long, WhStockOutGoods> shopGoodsOutMap = new HashMap<>();
        List<WhStockOut> outList = stockOutService.getByOrderCodeV2(code);
        if (CollectionUtils.isNotEmpty(outList)) {
            List<String> outCodes = outList.stream().map(WhStockOut::getOutCode).collect(Collectors.toList());
            // 获取发货商品记录
            Map<String, List<WhStockOutGoods>> outGoodsMap = new HashMap<>();
            List<WhStockOutGoods> goodsList = stockOutGoodsService.listByOutCodes(outCodes);
            if (CollectionUtils.isNotEmpty(goodsList)) {
                outGoodsMap = goodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getOutCode));
                shopGoodsOutMap = goodsList.stream().collect(Collectors.toMap(WhStockOutGoods::getShopCartId, Function.identity()));
            }
            // 获取发货快递记录
            List<WhStockOutDelivery> deliveryList = stockOutDeliveryService.listByOutCodes(outCodes);
            if (CollectionUtils.isNotEmpty(deliveryList)) {
                List<Long> deliveryIdList = deliveryList.stream().map(WhStockOutDelivery::getId).collect(Collectors.toList());
                List<WhStockOutDeliveryImg> deliveryImgList = stockOutDeliveryImgService.listByDeliveryIdIn(deliveryIdList);
                Map<Long, List<WhStockOutDeliveryImg>> imgMap = new HashMap<>();
                if (CollectionUtils.isNotEmpty(deliveryImgList)) {
                    imgMap = deliveryImgList.stream().collect(Collectors.groupingBy(WhStockOutDeliveryImg::getDeliveryId));
                }

                for (WhStockOutDelivery delivery : deliveryList) {
                    // 出库单发货记录
                    StockOutDeliveryDto deliveryObj = PurchaseUtil.getDeliveryObj(delivery, imgMap.get(delivery.getId()));
                    JSONArray goodsArr = new JSONArray();
                    if (delivery.getType().equals(1)) {
                        // 出库单的商品记录
                        List<WhStockOutGoods> outGoodsList = outGoodsMap.get(delivery.getOutCode());
                        if (CollectionUtils.isNotEmpty(outGoodsList)) {
                            for (WhStockOutGoods outGoods : outGoodsList) {
                                JSONObject goodsObj = new JSONObject();
                                goodsObj.put("goodsName", outGoods.getGoodsName());
                                goodsObj.put("goodsTypeName", outGoods.getGoodsTypeName());
                                goodsObj.put("num", outGoods.getNum());
                                goodsObj.put("freeDeliveryStatus", outGoods.getFreeDeliveryStatus());
                                goodsArr.add(goodsObj);
                            }
                        }
                    }
                    deliveryObj.setGoodsList(goodsArr);
                    express.add(deliveryObj);
                }
            }
        }

        JSONArray cartArr = new JSONArray();
        if (CollectionUtils.isNotEmpty(carts)) {
            if (finance) {
                List<Long> goodsTypeIdList = carts.stream().map(ShopCartDto::getGoodsTypeId).collect(Collectors.toList());
                Map<Long, GoodsWetPrice> goodsWetPriceMap = goodsWetPriceService.mapByGoodsTypeIdIn(goodsTypeIdList);
                Map<Long, Integer> outGoodsNum = stockOutGoodsService.getShopCartOutNum(code, 1, null);

                for (ShopCartDto cart : carts) {
                    GoodsWetPrice goodsWetPrice = goodsWetPriceMap.get(cart.getGoodsTypeId());
                    WhStockOutGoods stockOutGoods = shopGoodsOutMap.get(cart.getId());
                    String priceStr = "金额:" + cart.getPrice();
                    if (goodsWetPrice != null && goodsWetPrice.getWetAvePrice() != null) {
                        Double inPrice = DoubleUtil.mul(goodsWetPrice.getWetAvePrice(), cart.getBuyNum());
                        priceStr += "\n入库:" + inPrice;
                    }
                    if (stockOutGoods != null && stockOutGoods.getWetAvgPrice() != null) {
                        Double outPrice = DoubleUtil.mul(stockOutGoods.getWetAvgPrice(), cart.getBuyNum());
                        priceStr += "\n出库:" + outPrice;
                    }
                    cart.setPrice(priceStr);

                    JSONObject jsonObject = JsonUtil.toJsonObject(cart);
                    Integer outNum = NepUtil.nullToZero(outGoodsNum.get(cart.getId()));
                    jsonObject.put("outNum", outNum);
                    cartArr.add(jsonObject);
                }
            } else {
                Map<Long, Integer> outGoodsNum = stockOutGoodsService.getShopCartOutNum(code, 1, null);
                for (ShopCartDto cart : carts) {
                    JSONObject jsonObject = JsonUtil.toJsonObject(cart);
                    Integer outNum = NepUtil.nullToZero(outGoodsNum.get(cart.getId()));
                    jsonObject.put("outNum", outNum);
                    cartArr.add(jsonObject);
                }
            }
        }

        // 订单操作日志
        List<OrderFormLog> logs = orderFormLogService.getByCode(code);

        FormDto dto = new FormDto();
        dto.setOrderForm(form);
        dto.setShopCartList(cartArr);
        dto.setLogList(logs);
        dto.setExpressArr(express);
        dto.setImageList(imageList);
        return R.ok(dto);
    }

    @ApiOperation(value = "判断能否取消订单")
    @PostMapping(value = "/judgeCancel", produces = "application/json; charset=UTF-8")
    public R<Object> judgeCancel(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form) return R.fail("订单获取失败");

        // 联合订单号，有多个订单创建联合订单号

        Integer status = form.getStatus();

        ArrayList<Integer> statusList = Lists.newArrayList(EnumType.OrderForm.WAIT_CONFIRM.getType(), EnumType.OrderForm.WAIT_PAY.getType());
        if (!statusList.contains(status))
            return R.fail("订单状态不符合要求订单当前状态：" + EnumType.OrderForm.getStatus(status));

        return R.ok();
    }


    @ApiOperation(value = "取消订单")
    @PostMapping(value = "/cancel", produces = "application/json; charset=UTF-8")
    public R<Object> cancel(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null) return R.fail("获取失败");

        Integer status = form.getStatus();

        ArrayList<Integer> statusList = Lists.newArrayList(EnumType.OrderForm.WAIT_CONFIRM.getType(), EnumType.OrderForm.WAIT_PAY.getType());
        if (!statusList.contains(status))
            return R.fail("订单状态不符合订单状态：" + EnumType.OrderForm.getStatus(status));

        // 取消订单
        boolean l = this.orderFormService.cancelOrder(null, code);
        if (!l)
            return R.fail("修改失败订单状态：" + EnumType.OrderForm.getStatus(form.getStatus()));

        // 取消购物车记录
        shopCartService.cancelShopCarts(null, code);

        // 添加订单操作记录
        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.CANCEL.getType(), "取消订单", remark);
        orderFormLogService.saveOrUpdate(log);

        // 删除redis过期自动取消的商城订单号
        OrderFormV3Util.deleteMallCodeFromRedisExpire(code);

        return R.ok();
    }

    @Transactional(rollbackFor = DescribeException.class)
    @ApiOperation(value = "确认订单")
    @PostMapping(value = "/confirm", produces = "application/json; charset=UTF-8")
    public R<Object> confirm(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("获取订单失败");
        if (!code.startsWith(EnumType.CodeCategory.TOB.getType()))
            return R.fail("非toB订单不能支付");

        // 非自营门店，走支付流程
        boolean l = this.orderFormService.confirmOrder(code);
        if (!l)
            return R.fail("修改失败订单状态：" + EnumType.OrderForm.getStatus(form.getStatus()));

        // 更新购物车记录状态
        shopCartService.confirmShopCart(code);
        // 添加订单操作记录
        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.WAIT_PAY.getType(), "门店订单已确认，待付款", remark);
        orderFormLogService.saveOrUpdate(log);

        return R.ok();
    }


    @ApiOperation(value = "订单待操作数量")
    @PostMapping(value = "/getOperands", produces = "application/json; charset=UTF-8")
    public R<Object> getOperands(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "角色类型 1.仓库 2.财务 3.经理") @RequestParam(value = "roleType", required = false) Integer roleType
    ) {
        List<Long> list = orderFormService.getOperands(roleType);
        OperandsDto dto = new OperandsDto();
        if (CollectionUtils.isNotEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                Long aLong = NepUtil.nullToZero(list.get(i));
                if (i == 0) {
                    dto.setConfirmNum(aLong);
                } else if (i == 1) {
                    dto.setSendNum(aLong);
                }
            }
            dto.setTotal(dto.getConfirmNum() + dto.getSendNum());
        }
        return R.ok(dto);
    }

    @ApiOperation(value = "修改订单的签单人信息")
    @PostMapping(value = "/mdySaleStaff", produces = "application/json; charset=UTF-8")
    public R<Object> mdySaleStaff(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "签单人uid") @RequestParam(value = "saleStaffUid") String saleStaffUid,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");

        if (TimeUtil.moreThanDay(form.getAddTime(), 180))
            return R.fail("订单提交超过半年，不能操作");

        HeadStaff headStaff = headStaffService.getByUid(saleStaffUid);
        if (headStaff == null)
            return R.fail("签单人信息获取失败");

        boolean l = orderFormService.updateSaleStaff(code, saleStaffUid, headStaff.getUserName(), headStaff.getPhone());
        if (!l)
            return R.fail("修改失败");

        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.MDY_SALE_STAFF.getType(), "更改签单人为：" + headStaff.getUserName(), remark);
        orderFormLogService.saveOrUpdate(log);

        return R.ok();
    }


    @ApiOperation(value = "获取订单的发货信息")
    @PostMapping(value = "/getOrderExpress", produces = "application/json; charset=UTF-8")
    public R<Object> getOrderExpress(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null) return R.fail("获取订单失败");

        // 获取快递信息
        JSONArray express = new JSONArray();
        if (StringUtils.isNotBlank(form.getExpressCode())) {
            // 旧版订单快递信息
            List<String> strings = Arrays.stream(StringUtils.split(form.getExpressCode(), ";")).collect(Collectors.toList());
            for (String string : strings) {
                if (StringUtils.isNotBlank(string)) {
                    String[] split = StringUtils.split(string, "*");
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("expressCode", split[0]);
                    jsonObject.put("expressName", split[1]);
                    jsonObject.put("expressPrice", null);
                    jsonObject.put("expressTime", null);
                    jsonObject.put("goodsList", new JSONArray());
                    express.add(jsonObject);
                }
            }
        } else {
            // 获取发货完成的出库单
            List<WhStockOut> outList = stockOutService.getByOrderCodeV2(code);
            if (CollectionUtils.isNotEmpty(outList)) {
                List<String> outCodes = outList.stream().map(WhStockOut::getOutCode).collect(Collectors.toList());

                // 获取发货商品记录
                Map<String, List<WhStockOutGoods>> outGoodsMap = new HashMap<>();
                List<WhStockOutGoods> goodsList = stockOutGoodsService.listByOutCodes(outCodes);
                if (CollectionUtils.isNotEmpty(goodsList)) {
                    outGoodsMap = goodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getOutCode));
                }

                // 获取发货快递记录
                List<WhStockOutDelivery> deliveryList = stockOutDeliveryService.listByOutCodes(outCodes);
                if (CollectionUtils.isNotEmpty(deliveryList)) {
                    for (WhStockOutDelivery delivery : deliveryList) {
                        // 出库单发货记录
                        StockOutDeliveryDto deliveryObj = PurchaseUtil.getDeliveryObj(delivery, null);
                        JSONArray goodsArr = new JSONArray();
                        if (delivery.getType().equals(1)) {
                            // 出库单的商品记录
                            List<WhStockOutGoods> outGoodsList = outGoodsMap.get(delivery.getOutCode());
                            if (CollectionUtils.isNotEmpty(outGoodsList)) {
                                for (WhStockOutGoods outGoods : outGoodsList) {
                                    JSONObject goodsObj = new JSONObject();
                                    goodsObj.put("goodsName", outGoods.getGoodsName());
                                    goodsObj.put("goodsTypeName", outGoods.getGoodsTypeName());
                                    goodsObj.put("num", outGoods.getNum());
                                    goodsObj.put("freeDeliveryStatus", outGoods.getFreeDeliveryStatus());
                                    goodsArr.add(goodsObj);
                                }
                            }
                        }
                        deliveryObj.setGoodsList(goodsArr);
                        express.add(deliveryObj);
                    }
                }

            }
        }

        return R.ok(express);
    }


    @ApiOperation(value = "恢复已取消的订单")
    @PostMapping(value = "/resume", produces = "application/json; charset=UTF-8")
    public R<Object> resume(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form) return R.fail("订单获取失败");

        Stores stores = storesService.getByUid(form.getStoreUid());
        if (null == stores)
            return R.fail("门店信息获取失败");

        Date editTime = form.getEditTime();
        Date time = DateUtil.getNowDatetime();

        // 判断订单时间
        if (!DateUtils.isSameDay(editTime, time))
            return R.fail("不是当天订单不能恢复");

        // 验证订单的代金券
        if (null != form.getCouponStaffId()) {
            List<OrderForm> list = orderFormService.getNoPayUseCouponForm(form.getStoreUid(), form.getStaffUid(), form.getCouponStaffId(), code);
            if (CollectionUtils.isNotEmpty(list))
                return R.fail("该订单使用的代金券已被其他订单占用，不能恢复");
            CouponStaff couponStaff = couponStaffService.getStaffCoupon(form.getStoreUid(), form.getStaffUid(), form.getCouponStaffId());
            if (null == couponStaff)
                return R.fail("该订单使用的代金券已失效，不能恢复");
        }

        // 验证购物车记录
        List<ShopCart> mainAndChildCartList = shopCartService.mainListAndChildListByCode(code);
        if (CollectionUtils.isEmpty(mainAndChildCartList))
            return R.fail("购物车内的商品获取失败");
        // 判断已取消的购物车记录是否有效
        String resultMsg = OrderFormV3Util.verifyResumeShopCarts(AccountUtil.isSelfStore(stores.getOperationType()), mainAndChildCartList);
        if (StringUtils.isNotBlank(resultMsg))
            return R.fail(resultMsg);

        Integer status = EnumType.OrderForm.WAIT_CONFIRM.getType();
        String statusName = EnumType.OrderForm.WAIT_CONFIRM.getName();
        // 恢复订单
        boolean l = this.orderFormService.resumeOrder(code, status, statusName);
        if (!l)
            return R.fail("修改失败订单状态：" + EnumType.OrderForm.getStatus(form.getStatus()));
        // 恢复购物车记录
        shopCartService.resumeShopCarts(code, status, statusName);

        // 添加订单操作记录
        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.RESUME.getType(), "恢复已取消的订单", remark);
        orderFormLogService.saveOrUpdate(log);

        // 未付款的商城订单过期后自动取消
        OrderFormV3Util.addMallCodeToRedisExpire(code);

        return R.ok();
    }


    @ApiOperation(value = "发货时获取订单的商品")
    @PostMapping(value = "/getGoodsToExpress", produces = "application/json; charset=UTF-8")
    public R<Object> getGoodsToExpress(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "出库方式") @RequestParam(value = "stockOutMethod", required = false) Integer stockOutMethod
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null) return R.fail("订单获取失败");
        Integer status = form.getStatus();

        if (!status.equals(EnumType.OrderForm.WAIT_SEND.getType()) && !status.equals(EnumType.OrderForm.SENDING.getType())
                && !status.equals(EnumType.OrderForm.OK.getType()))
            return R.fail("订单状态不符合订单状态：" + EnumType.OrderForm.getStatus(status));

        List<ShopCartSendDto> list = shopCartService.listToExpress(Collections.singletonList(code), stockOutMethod);
        if (CollectionUtils.isEmpty(list))
            return R.fail("购物车商品记录获取失败");

        List<Long> goodsTypeIds = list.stream().map(ShopCartSendDto::getGoodsTypeId).distinct().collect(Collectors.toList());

        // 获取商品的库存数量
        Map<Long, Integer> stockNumMap = stockInService.getStockNumMap(goodsTypeIds);

        // 计算该商品，已完成的出库数量和待完成的出库数量
        List<WhStockOutGoods> outGoodsList = stockOutGoodsService.getByOrderCodeV2(code, stockOutMethod, Lists.newArrayList(1, 2));
        Map<Long, List<WhStockOutGoods>> goodsMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(outGoodsList)) {
            goodsMap = outGoodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getShopCartId));
        }

        for (ShopCartSendDto dto : list) {
            // 库存数量
            dto.setSaleNum(stockNumMap.get(dto.getGoodsTypeId()));

            // 出库数量
            int outNum = 0;
            // 出库中的数量
            int saveOutNum = 0;
            List<WhStockOutGoods> goodsList = goodsMap.get(dto.getShopCartId());
            if (CollectionUtils.isNotEmpty(goodsList)) {
                for (WhStockOutGoods outGoods : goodsList) {
                    int num = NepUtil.nullToZero(outGoods.getNum());
                    if (outGoods.getStatus().equals(1)) {
                        // 出库完成
                        outNum += num;
                    } else if (outGoods.getStatus().equals(2)) {
                        // 保存中
                        saveOutNum += num;
                    }
                }
            }
            dto.setOutNum(outNum);
            dto.setSaveOutNum(saveOutNum);
        }

        return R.ok(list);
    }

    @GlobalTransactional
    @ApiOperation(value = "商品订单-通过银行转账支付")
    @PostMapping(value = "/confirmBankPay", produces = "application/json; charset=UTF-8")
    public R<Object> confirmBankPay(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        // 查询充值订单流水号
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("获取订单失败");
        if (!form.getStatus().equals(EnumType.OrderForm.APPLY_BANKPAY.getType()))
            return R.fail("订单状态不符合");
        if (form.getFinanceStatus() != null) {
            if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_OK.getType())) {
                return R.fail("财务审核通过，不能操作");
            } else if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType())) {
                return R.fail("财务审核拒绝，不能操作");
            }
        }

        Stores stores = storesService.getByUid(form.getStoreUid());
        if (null == stores)
            return R.fail("门店信息获取失败");

        // 验证商品订单是否有效
        String msg = OrderFormV3Util.verifyOrderForm(form, true, AccountUtil.isSelfStore(stores.getOperationType()));
        if (StringUtils.isNotBlank(msg))
            return R.fail(msg);
        if (!code.startsWith(EnumType.CodeCategory.TOB.getType()))
            return R.fail("非toB订单不能支付");

        String remarkHead = "银行转账审核通过";
        if (StringUtils.isNotBlank(remark))
            remark = remarkHead + "，" + remark;
        else
            remark = remarkHead;

        // 订单支付成功后的处理
        msg = payOkFeignService.bankGoodsOrder(headStaffUid, headStaffName, code, form.getPayTime(), StringUtils.isNotBlank(remark) ? remark : "");
        if (StringUtils.isNotBlank(msg))
            throw new DescribeException(500, msg);

        // 企业微信获取access_token
        String accessToken = getQiWeiAccessToken();
        // 发送钉钉机器人群消息通知
        QyWeiXinUtil.sendToChat(accessToken, financechatid, QyWeixinMsg.bankCheckMsg(serverUrl, form.getStoreName(), form.getCode()));

        return R.ok();
    }

    @ApiOperation(value = "商品订单-驳回银行转账支付")
    @PostMapping(value = "/refuseBankPay", produces = "application/json; charset=UTF-8")
    public R<Object> refuseBankPay(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark") String remark
    ) throws Exception {
        if (StringUtils.isBlank(remark))
            return R.fail("请填写备注");

        // 查询充值订单流水号
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (!form.getStatus().equals(EnumType.OrderForm.APPLY_BANKPAY.getType()))
            return R.fail("订单状态不符合");
        if (form.getFinanceStatus() != null) {
            if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_OK.getType())) {
                return R.fail("财务审核通过，不能操作");
            } else if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType())) {
                return R.fail("财务审核拒绝，不能操作");
            }
        }

        boolean l = orderFormService.refuseBankPay(code);
        if (!l) return R.fail("操作失败");

        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.REFUSE_BANKPAY.getType(), "银行转账被驳回", remark);
        orderFormLogService.saveOrUpdate(log);

        return R.ok();
    }

    @Transactional(rollbackFor = DescribeException.class)
    @ApiOperation(value = "商品订单-撤回银行转账支付")
    @PostMapping(value = "/revocationBankPay", produces = "application/json; charset=UTF-8")
    public R<Object> revocationBankPay(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        // 查询充值订单流水号
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (!form.getStatus().equals(EnumType.OrderForm.WAIT_SEND.getType()))
            return R.fail("订单状态不符合");
        if (form.getFinanceStatus() != null) {
            if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_OK.getType())) {
                return R.fail("财务审核通过，不能操作");
            } else if (form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType())) {
                return R.fail("财务审核拒绝，不能操作");
            }
        }

        OrderFormLog formLog = orderFormLogService.getByCodeAndStatus(code, EnumType.OrderForm.WAIT_SEND.getType());
        if (null == formLog)
            return R.fail("订单付款的日志记录获取失败");
        // 超过30天不能修改
        if (TimeUtil.moreThanDay(formLog.getAddTime(), 30))
            return R.fail("审核通过已超过30天，不能撤回");

        // 判断商品发货情况
        long outLCnt = stockOutService.getOkOutCnt(code);
        if (outLCnt > 0)
            return R.fail("已经开始发货，不能撤回");

        // 商品数据
        List<ShopCart> shopCartList = shopCartService.getByCode(form.getStaffUid(), code);
        if (CollectionUtils.isEmpty(shopCartList))
            return R.fail("订单的购物车记录获取失败");
        List<Long> goodsTypeIds = shopCartList.stream().map(ShopCart::getGoodsTypeId).distinct().collect(Collectors.toList());
        List<GoodsType> goodsTypeList = goodsTypeService.listByIds(goodsTypeIds);
        if (CollectionUtils.isEmpty(goodsTypeList))
            return R.fail("订单内的商品规格不存在或缺失");

        // 撤回处理订单
        boolean l = orderFormService.revocationBankPay(code);
        if (!l)
            return R.fail("订单撤回失败");

        // 撤回处理购物车记录
        l = shopCartService.revocationPayed(code);
        if (!l)
            throw new DescribeException("购物车记录，撤回失败");

        // 删除保存中的出库订单
        stockOutService.dropSaveOuts(code);
        // 删除保存中的出库商品详情
        stockOutGoodsService.dropSaveOutGoods(code);

        // 撤回收支记录
        l = balanceLogService.revocationPayed(code, headStaffUid, headStaffName, "撤回银行转账");
        if (!l)
            throw new DescribeException("收支流水记录，撤回失败");

        // 处理代金券
        if (null != form.getCouponStaffId()) {
            CouponStaff couponStaff = couponStaffService.getById(form.getCouponStaffId());
            if (null == couponStaff)
                throw new DescribeException("订单使用的代金券获取失败");

            List<CouponStaffLog> couponLogs = couponStaffLogService.getByCode(code, 1);
            if (CollectionUtils.isEmpty(couponLogs))
                throw new DescribeException("代金券使用记录获取失败");
            CouponStaffLog couponStaffLog = couponLogs.get(0);

            l = couponStaffLogService.revocationPayed(code, form.getCouponStaffId());
            if (!l)
                throw new DescribeException("代金券使用记录，撤回失败");

            double usableMoney = NepUtil.nullToZero(couponStaff.getUsableMoney());
            double usedMoney = NepUtil.nullToZero(couponStaff.getUsedMoney());
            double logUseMoney = NepUtil.nullToZero(couponStaffLog.getUseMoney());
            // 撤回更新员工代金券的可用金额和已使用金额
            l = couponStaffService.revocationMoney(form.getStoreUid(), form.getStaffUid(), form.getCouponStaffId(),
                    DoubleUtil.sub(usedMoney, logUseMoney), DoubleUtil.sum(usableMoney, logUseMoney));
            if (!l)
                throw new DescribeException("更新代金券使用的金额失败");
        }

        // 添加订单操作记录
        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                EnumType.OrderForm.REVOCATION_BANKPAY.getType(), "撤回审核通过的银行转账", remark);
        orderFormLogService.saveOrUpdate(log);

        return R.ok();
    }


    @ApiOperation(value = "商品订单-财务审核付款方式是银行转账的订单")
    @PostMapping(value = "/financeCheckForm", produces = "application/json; charset=UTF-8")
    public R<Object> financeCheckForm(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "财务审核状态 90.待财务审核 91.财务审核通过 92.财务审核拒绝") @RequestParam(value = "financeStatus") Integer financeStatus,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        int financeCheckOkType = EnumType.OrderForm.FINANCE_CHECK_OK.getType();
        int financeCheckRefuseType = EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType();

        if (null == financeStatus)
            return R.fail("请选择审核状态");
        if (!financeStatus.equals(financeCheckOkType) && !financeStatus.equals(financeCheckRefuseType))
            return R.fail("审核状态错误");
        if (financeStatus.equals(financeCheckRefuseType) && StringUtils.isBlank(remark))
            return R.fail("请填写备注");

        // 查询充值订单流水号
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (form.getFinanceStatus() != null) {
            if (form.getFinanceStatus().equals(EnumType.OrderForm.SYSTEM_CHECK_OK.getType()))
                return R.fail("系统审核已通过，不能操作");
            if (form.getFinanceStatus().equals(financeCheckOkType)) {
                return R.fail("财务审核已通过，不能操作！");
            }
        }
        if (!form.getStatus().equals(EnumType.OrderForm.OK.getType()) && !form.getStatus().equals(EnumType.OrderForm.SENDING.getType())
                && !form.getStatus().equals(EnumType.OrderForm.WAIT_SEND.getType()) && !form.getStatus().equals(EnumType.OrderForm.APPLY_BANKPAY.getType())
        ) {
            return R.fail("订单状态不符合！状态：" + form.getStatusName());
        }

        Stores stores = storesService.getByUid(form.getStoreUid());
        if (null == stores)
            return R.fail("门店信息获取失败");

        String financeStatusName = EnumType.OrderForm.getStatus(financeStatus);
        boolean l = orderFormService.updateFinanceStatus(code, financeStatus, financeStatusName);
        if (!l) {
            return R.fail("审核失败");
        }

        // 添加订单操作记录
        OrderFormLog log = OrderFormV3Util.newHeadLog(
                form.getCode(), headStaffUid, headStaffName, financeStatus, financeStatusName, remark
        );
        orderFormLogService.saveOrUpdate(log);

        if (financeStatus.equals(financeCheckOkType)) {
            // 财务审批通过且未发货
            if (form.getStatus().equals(EnumType.OrderForm.APPLY_BANKPAY.getType())) {
                // 验证商品订单是否有效
                String msg = OrderFormV3Util.verifyOrderForm(form, true, AccountUtil.isSelfStore(stores.getOperationType()));
                if (StringUtils.isNotBlank(msg))
                    return R.fail(msg);
                if (!code.startsWith(EnumType.CodeCategory.TOB.getType()))
                    return R.fail("非toB订单不能支付");

                String remarkHead = "银行转账审核通过";
                remark = StringUtils.isNotBlank(remark) ? remarkHead + "，" + remark : remarkHead;

                // 订单支付成功后的处理
                msg = payOkFeignService.bankGoodsOrder(headStaffUid, headStaffName, code, form.getPayTime(), remark);
                if (StringUtils.isNotBlank(msg))
                    throw new DescribeException(500, msg);
            }
        } else {
            // 财务审批拒绝，发送群聊消息
            // 企业微信获取access_token
            String accessToken = getQiWeiAccessToken();
            // 发送企业微信消息
            String timeStr = DateUtil.getNowDatetimeStr();
            String staffName = StringUtils.isNotBlank(form.getSaleStaffName()) ? form.getSaleStaffName() : "";
            if (AccountUtil.isJointOperation(stores.getOperationType(), form.getStaffUid())) {
                // 判断是不是采购或联营店订单
                QyWeiXinUtil.sendToUser(accessToken, agentid, thirdUserid, QyWeixinMsg.financeCheckFormMsg(serverUrl, 2, remark, staffName, form.getStoreName(), form.getCode(), timeStr));
            } else {
                QyWeiXinUtil.sendToChat(accessToken, mallchatid, QyWeixinMsg.financeCheckFormMsg(serverUrl, 1, remark, staffName, form.getStoreName(), form.getCode(), timeStr));
            }
        }

        return R.ok();
    }


    @GlobalTransactional
    @ApiOperation(value = "确认自营门店或赠品订单")
    @PostMapping(value = "/confirmSelfOrGiftOrder", produces = "application/json; charset=UTF-8")
    public R<Object> confirmSelfOrGiftOrder(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "备注") @RequestParam(value = "remark", required = false) String remark
    ) throws Exception {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("获取订单失败");
        if (!code.startsWith(EnumType.CodeCategory.TOB.getType()))
            return R.fail("非toB订单不能支付");

        Stores stores = storesService.getByUid(form.getStoreUid());
        if (null == stores)
            return R.fail("门店信息获取失败");

        if (form.getProjectType().equals(EnumType.BalanceProject.BUY_GOODS.getType())) {
            if (AccountUtil.isJointOperation(stores.getOperationType(), form.getStaffUid())) {
                // 联营店订单，走支付流程

                boolean l = this.orderFormService.confirmOrder(code);
                if (!l)
                    return R.fail("修改失败订单状态：" + EnumType.OrderForm.getStatus(form.getStatus()));

                // 更新购物车记录状态
                shopCartService.confirmShopCart(code);

                // 添加订单操作记录
                OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                        EnumType.OrderForm.WAIT_PAY.getType(), "门店订单已确认，待付款", remark);
                orderFormLogService.saveOrUpdate(log);
            } else if (AccountUtil.isSelfStore(stores.getOperationType())) {
                // 自营门店，走发货流程

                // 验证商品订单是否有效
                String msg = OrderFormV3Util.verifyOrderForm(form, false, AccountUtil.isSelfStore(stores.getOperationType()));
                if (StringUtils.isNotBlank(msg))
                    return R.fail(msg);

                // 订单支付成功后的处理
                String resultMsg = payOkFeignService.selfGoodsOrder(headStaffUid, headStaffName, code, StringUtils.isNotBlank(remark) ? remark : "");
                if (StringUtils.isNotBlank(resultMsg))
                    throw new DescribeException(500, resultMsg);
                // 更新支付渠道
                orderFormService.updatePayChannelCode(form.getStaffUid(), code, EnumType.PayChannelCode.SELF.getType());
            }
        } else if (form.getProjectType().equals(EnumType.BalanceProject.GIFT_GOODS.getType())) {
            // 赠品订单
            if (StringUtils.isBlank(form.getGiftImg()))
                return R.fail("赠品凭证图片未上传");

            // 验证订单有效性
            String msg = OrderFormV3Util.verifyOrderForm(form, false, true);
            if (StringUtils.isNotBlank(msg))
                return R.fail(msg);

            // 订单支付成功后的处理
            String resultMsg = payOkFeignService.giftGoodsOrder(headStaffUid, headStaffName, code, StringUtils.isNotBlank(remark) ? remark : "");
            if (StringUtils.isNotBlank(resultMsg))
                throw new DescribeException(500, resultMsg);

            // 企业微信获取access_token
            String accessToken = getQiWeiAccessToken();
            // 发送企业微信消息
            QyWeiXinUtil.sendToChat(accessToken, mallchatid, QyWeixinMsg.confirmOkOrderMsg(serverUrl, form.getStoreName(), form.getCode(), form.getProjectType(), DateUtil.getNowDatetimeStr()));
        } else {
            return R.fail("订单不是自营门店订单或赠品订单");
        }

        return R.ok();
    }


    @ApiOperation(value = "银行转账截图再次提交财务审核")
    @PostMapping(value = "/bankPayImgSubmitToFinance", produces = "application/json; charset=UTF-8")
    public R<Object> bankPayImgSubmitToFinance(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (!form.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()))
            return R.fail("订单的支付方式不是银行转账");
        if (!form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType()))
            return R.fail("不是财务审核拒绝状态，不能操作");

        int checkWaitType = EnumType.OrderForm.FINANCE_CHECK_WAIT.getType();
        String checkWaitName = EnumType.OrderForm.FINANCE_CHECK_WAIT.getName();
        boolean l = orderFormService.updateFinanceStatus(code, checkWaitType, checkWaitName);
        if (!l)
            return R.fail("操作失败");

        // 添加订单操作记录
        int financeSubmitType = EnumType.OrderForm.FINANCE_SUBMIT.getType();
        String financeSubmitName = EnumType.OrderForm.FINANCE_SUBMIT.getName();
        OrderFormLog log = OrderFormV3Util.newHeadLog(form.getCode(), headStaffUid, headStaffName,
                financeSubmitType, financeSubmitName, "");
        orderFormLogService.save(log);

        return R.ok();
    }


    @ApiOperation(value = "获取订单的银行转账截图")
    @PostMapping(value = "/getBankPayImg", produces = "application/json; charset=UTF-8")
    public R<Object> getBankPayImg(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (!form.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()))
            return R.fail("订单的支付方式不是银行转账");

        List<OrderFormImage> list = orderFormImageService.getByOrderCode(code);
        return R.ok(list);
    }


    @ApiOperation(value = "银行转账，上传银行转账截图")
    @PostMapping(value = "/uploadBankPayImg", produces = "application/json; charset=UTF-8")
    public R<Object> uploadBankPayImg(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "图片文件") @RequestPart(value = "file") MultipartFile file
    ) throws Exception {
        OrderForm form = orderFormService.getByCode(null, code);
        if (null == form)
            return R.fail("订单获取失败");
        if (!form.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()))
            return R.fail("订单的支付通道不是银行转账");
        if (!form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType()))
            return R.fail("订单的财务审核状态不是财务审核拒绝");

        long imgNum = orderFormImageService.getImgNum(code);
        if (imgNum >= 10)
            return R.fail("图片最多上传10张");

        // 上传图片到服务器
        R<String> r = LocalFileUtil.uploadImg(LocalFileUtil.orderFormBaseDir, file);
        if (R.isError(r))
            return R.fail(r.getMsg());

        String imageUrl = r.getData();
        OrderFormImage image = new OrderFormImage();
        image.setOrderCode(code);
        image.setStaffUid(form.getStaffUid());
        image.setImgUrl(imageUrl);
        image.setAddStaffUid(headStaffUid);
        image.setAddStaffName(headStaffName);
        orderFormImageService.saveOrUpdate(image);

        JSONObject object = new JSONObject();
        object.put("id", image.getId());
        object.put("imgUrl", imageUrl);
        return R.ok(object);
    }


    @ApiOperation(value = "删除订单的银行转账截图")
    @PostMapping(value = "/dropBankPayImg", produces = "application/json; charset=UTF-8")
    public R<Object> dropBankPayImg(
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号") @RequestParam(value = "code") String code,
            @ApiParam(value = "图片id") @RequestParam(value = "imgId") Long imgId
    ) {
        OrderForm form = orderFormService.getByCode(null, code);
        if (form == null)
            return R.fail("订单获取失败");
        if (!form.getPayChannelCode().equals(EnumType.PayChannelCode.BANK.getType()))
            return R.fail("订单的支付通道不是银行转账");
        if (!form.getFinanceStatus().equals(EnumType.OrderForm.FINANCE_CHECK_REFUSE.getType()))
            return R.fail("订单的财务审核状态不是财务审核拒绝");

        OrderFormImage image = orderFormImageService.getById(imgId);
        if (null == image)
            return R.fail("图片不存在");

        long l = orderFormImageService.dropByOrderCodeAndId(code, imgId);
        if (l <= 0)
            return R.failDel();

        // 删除服务器上的截图
        if (StringUtils.isNotBlank(image.getImgUrl())) {
            LocalFileUtil.deleteImg(LocalFileUtil.orderFormBaseDir, image.getImgUrl());
        }
        return R.ok();
    }

    @ApiOperation(value = "导出已付款的订单")
    @PostMapping(value = "/exportList", produces = "application/vnd.ms-excel;charset=UTF-8")
    public void exportList(
            HttpServletResponse response,
            @ApiParam(value = "总部员工uid") @RequestParam(value = "headStaffUid") String headStaffUid,
            @ApiParam(value = "总部员工名") @RequestParam(value = "headStaffName") String headStaffName,
            @ApiParam(value = "订单号/用户姓名/手机号") @RequestParam(value = "keyword", required = false) String keyword,
            @ApiParam(value = "商品名称") @RequestParam(value = "goodsName", required = false) String goodsName,
            @ApiParam(value = "项目类型") @RequestParam(value = "projectType", required = false) Integer projectType,
            @ApiParam(value = "支付类型") @RequestParam(value = "payChannelCode", required = false) String payChannelCode,
            @ApiParam(value = "角色类型 1.仓库 2.财务 3.经理 4.小花") @RequestParam(value = "roleType", required = false) Integer roleType,
            @ApiParam(value = "状态 0.取消 1.完成 2.待确认 3.待付款 4.待发货") @RequestParam(value = "status", required = false) Integer status,
            @ApiParam(value = "财务审核状态 90.待财务审核 91.财务审核通过 92.财务审核拒绝 99.系统审核通过") @RequestParam(value = "financeStatus", required = false) Integer financeStatus,
            @ApiParam(value = "开始日期 yyyy-MM-dd") @RequestParam(value = "beginDate", required = false) String beginDate,
            @ApiParam(value = "结束日期 yyyy-MM-dd") @RequestParam(value = "endDate", required = false) String endDate
    ) throws Exception {
        // 默认获取本月的第一天和最后一天
        if (StringUtils.isBlank(beginDate) || StringUtils.isBlank(endDate)) {
            Date monthMinDate = DateUtil.getMonthMinDate();
            Date monthMaxDate = DateUtil.getMonthMaxDate();
            beginDate = DateUtil.getDefaultTime2DateStr(monthMinDate);
            endDate = DateUtil.getDefaultTime2DateStr(monthMaxDate);
        }

        List<OrderForm> list = orderFormService.getExportList(
                keyword, projectType, roleType, status, beginDate, endDate, payChannelCode, financeStatus, goodsName
        );
        if (CollectionUtils.isEmpty(list))
            throw new DescribeException("未查询到数据");

        List<String> codeList = new ArrayList<>();
        List<String> uidList = new ArrayList<>();
        for (OrderForm orderForm : list) {
            codeList.add(orderForm.getCode());

            uidList.add(orderForm.getStaffUid());
        }

        List<Staff> staffList = staffService.getByUids(uidList);
        Map<String, String> staffMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(staffList)) {
            staffMap = staffList.stream().parallel()
                    .collect(HashMap::new, (n, v) -> n.put(v.getUid(), StringUtils.isBlank(v.getFullName()) ? "" : v.getFullName()), HashMap::putAll);
        }

        List<ShopCart> shopCartList = shopCartService.getByCodes(null, codeList);
        if (CollectionUtils.isEmpty(shopCartList))
            throw new DescribeException("未查询到购物车数据");
        List<Long> goodsTypeIdList = shopCartList.stream().map(ShopCart::getGoodsTypeId).collect(Collectors.toList());

        Map<String, List<ShopCart>> shopCartMap = shopCartList.stream().collect(Collectors.groupingBy(ShopCart::getCode));

        List<WhStockOutGoods> outGoodsList = stockOutGoodsService.listByOrderCodesAndStatus(codeList, 1);
        Map<Long, List<WhStockOutGoods>> outGoodsMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(outGoodsList)) {
            outGoodsMap = outGoodsList.stream().collect(Collectors.groupingBy(WhStockOutGoods::getShopCartId));
        }

        Map<Long, GoodsWetPrice> goodsWetPriceMap = goodsWetPriceService.mapByGoodsTypeIdIn(goodsTypeIdList);

        List<ShopCartAndOutGoodsVo> voList = new ArrayList<>();
        for (OrderForm form : list) {
            List<ShopCart> shopCarts = shopCartMap.get(form.getCode());

            for (int i = 0; i < shopCarts.size(); i++) {
                ShopCart shopCart = shopCarts.get(i);
                GoodsWetPrice goodsWetPrice = goodsWetPriceMap.get(shopCart.getGoodsTypeId());
                List<WhStockOutGoods> outGoods = outGoodsMap.get(shopCart.getId());

                ShopCartAndOutGoodsVo vo = setShopCartAndOutGoodsVo(form, shopCart, staffMap.get(form.getStaffUid()), i, outGoods, goodsWetPrice);
                voList.add(vo);
            }

            voList.add(new ShopCartAndOutGoodsVo());
        }

        String fileName = "订单记录" + beginDate + "至" + endDate;
        ExcelHandler.exportExcel(response, voList, ShopCartAndOutGoodsVo.class, fileName, "订单记录", true);
    }

    /**
     * 生成导出数据
     */
    private ShopCartAndOutGoodsVo setShopCartAndOutGoodsVo(
            OrderForm form, ShopCart shopCart, String staffName, int i, List<WhStockOutGoods> outGoods, GoodsWetPrice goodsWetPrice
    ) {
        ShopCartAndOutGoodsVo data = new ShopCartAndOutGoodsVo();
        data.setStoreName(form.getStoreName());
        data.setCode(form.getCode());
        if (form.getCorpUid().equals(AccountUtil.SELF_CORP_UID)) {
            data.setSelfStatus("自营门店");
        } else if (form.getCorpUid().equals(AccountUtil.SELF_CORP_UID_T)) {
            data.setSelfStatus("演示门店");
        } else {
            data.setSelfStatus("加盟门店");
        }
        data.setStaffName(staffName);
        data.setProjectName(form.getProjectName());
        if (i == 0) {
            data.setAddress(form.getAddress());
            data.setPrice(form.getPrice());
            data.setCouponPrice(form.getCouponPrice());
            data.setPayPrice(form.getPayPrice());
        }
        data.setPayTime(form.getPayTime());
        if (null != form.getPayType())
            data.setPayType(EnumType.PayType.getTypeName(form.getPayType()));

        Integer outNum = 0;
        // 出库金额
        double outPrice = 0d;
        if (CollectionUtils.isNotEmpty(outGoods)) {
            for (WhStockOutGoods outGood : outGoods) {
                outNum += outGood.getNum();
                // 出库金额
                double mul = DoubleUtil.mul(outGood.getNum(), NepUtil.nullToZero(outGood.getWetAvgPrice()));
                outPrice = DoubleUtil.sum(outPrice, mul);
            }
        }
        if (outNum > 0) {
            data.setStatusName("已出库");
        } else {
            data.setStatusName(form.getStatusName());
        }

        // 入库金额
        double inPrice = 0;
        if (goodsWetPrice != null)
            inPrice = DoubleUtil.mul(NepUtil.nullToZero(goodsWetPrice.getWetAvePrice()), shopCart.getBuyNum());

        data.setGoodsName(shopCart.getGoodsName());
        data.setGoodsTypeName(shopCart.getGoodsTypeName());
        data.setBuyNum(shopCart.getBuyNum());
        data.setOutNum(outNum);
        data.setGoodsStatusName(shopCart.getStatusName());

        data.setGoodsPrice(shopCart.getPrice());
        data.setOutPrice(outPrice);
        data.setInPrice(inPrice);

        return data;
    }


    /**
     * 企业微信获取access_token
     */
    private String getQiWeiAccessToken() {
        String accessToken = (String) redisService.get(RedisParam.QYWEINXIN_ACCESS_TOKEN);
        if (StringUtils.isBlank(accessToken)) {
            AccessTokenDto dto = QyWeiXinUtil.getAccessToken(corpid, secret);
            if (dto != null) {
                accessToken = dto.getAccess_token();
                int expires_in = dto.getExpires_in();
                // redis缓存accessToken
                redisService.set(RedisParam.QYWEINXIN_ACCESS_TOKEN, accessToken, expires_in);
            }
        }
        return accessToken;
    }

}